﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace ExcelToUnityScript.UnityScript
{

    public class CSVClassData
    {
        public CSVFiledInfo[] fileds;
    }

    /// <summary>
    /// 字段信息
    /// </summary>
    public class CSVFiledInfo
    {
        /// <summary>
        /// 起始位置
        /// </summary>
        public int Index;
        /// <summary>
        /// 名称
        /// </summary>
        public string Name;
        /// <summary>
        /// 类型
        /// </summary>
        public string Type;
        /// <summary>
        /// 是否为数组
        /// </summary>
        public bool IsArray;
        /// <summary>
        /// 数组长度
        /// </summary>
        public int Length;

        private int valueIndex = 0;
        public string value;
        public string[] values;


        public int GetValueIndex { get => valueIndex; }
        public string Put(int index)
        {
            return values[index];
        }
        public CSVFiledInfo Clone()
        {
            CSVFiledInfo info = new CSVFiledInfo();
            info.Index = Index;
            info.Name = Name;
            info.Type = Type;
            info.IsArray = IsArray;
            info.Length = Length;
            info.valueIndex = 0;
            return info;
        }
        public void Push(string v)
        {
            if (IsArray)
            {
                if (values == null)
                    values = new string[Length];
                values[valueIndex++] = v;
            }
            else
            {
                value = v;
            }
        }
    }
    /// <summary>
    /// CSV 数据脚本生成
    /// </summary>
    public class CSVScript
    {

        //string[] EncodingType = new string[] { "UTF8", "Unicode", "ANSI" };
        /// <summary>
        /// 忽略行
        /// </summary>
        int IgnoreNum = 1;

        /// <summary>
        /// 是否向上追溯
        /// </summary>
        bool IsBackUp;

        /// <summary>
        /// CSV 数据脚本生成
        /// </summary>
        /// <param name="IgnoreNum">忽略行数</param>
        /// <param name="IsBackUp">是否向上追溯</param>
        public CSVScript(int IgnoreNum = 1, bool IsBackUp = false)
        {
            this.IgnoreNum = IgnoreNum;
            this.IsBackUp = IsBackUp;
        }

        /// <summary>
        /// 验证字段类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private bool ValidationType(string t)
        {
            switch (t.ToLower())
            {
                case "string":
                case "bool":
                case "int":
                case "double":
                case "float":
                case "v2":
                case "v3":
                case "color":
                    return true;
            }

            return false;
        }
        /// <summary>
        /// 读取字段与类型
        /// </summary>
        public List<CSVFiledInfo> ReadFiled(string[] lines)
        {
            if (null == lines || lines.Length < 2)
                throw new Exception("数据为空或者行数小于2");

            List<CSVFiledInfo> lsFiledInfo = new List<CSVFiledInfo>();


            var types = lines[0].Split(',');
            var fileds = lines[1].Split(',');

            int length = fileds.Length;
            string _t = string.Empty;
            CSVFiledInfo filedInfo = null;

            for (int i = 0; i < length; i++)
            {
                var t = types[i];
                if (string.IsNullOrEmpty(t))
                {
                    if (string.IsNullOrEmpty(_t))
                        throw new Exception($"缺少类型：{fileds[i]}");

                }
                else
                {
                    if (!ValidationType(t))
                        throw new Exception($"字段类型拼写错误：{t}");


                    _t = t;
                }

                var f = fileds[i];
                if (string.IsNullOrEmpty(f))
                    throw new Exception($"在第{i + 1}个字段 不能为空");



                if (null == filedInfo)
                {
                    filedInfo = new CSVFiledInfo();
                    filedInfo.Index = i;
                    filedInfo.Name = f;
                    filedInfo.Type = _t;
                    lsFiledInfo.Add(filedInfo);
                }
                else
                {
                    if (filedInfo.Name.Equals(f))
                    {
                        if (!filedInfo.Type.Equals(_t))
                            throw new Exception($"字段名称一样但是类型不一致：{filedInfo.Name}  {filedInfo.Type} !== {_t}");

                        if (!filedInfo.IsArray)
                            filedInfo.Length = 1;
                        filedInfo.IsArray = true;
                        filedInfo.Length++;
                    }
                    else
                    {

                        filedInfo = new CSVFiledInfo();
                        filedInfo.Index = i;
                        filedInfo.Name = f;
                        filedInfo.Type = _t;
                        lsFiledInfo.Add(filedInfo);
                    }
                }
            }
            return lsFiledInfo;
        }

        private CSVFiledInfo FindFiled(List<CSVFiledInfo> filedInfo, int index)
        {
            int count = filedInfo.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                if (filedInfo[i].Index <= index)
                    return filedInfo[i];
            }

            return null;
        }
        /// <summary>
        /// 解析行
        /// </summary>
        /// <param name="lines"></param>
        public List<CSVClassData> ParseLine(string[] lines, List<CSVFiledInfo> lsfiledInfo)
        {


            List<CSVClassData> lsCsvClass = new List<CSVClassData>();

            int length = lines.Length;
            for (int i = 2; i < length; i++)
            {
                var line = lines[i];
                // 跳过注释行
                if (line.StartsWith("//") || line.StartsWith("/*"))
                    continue;

                var fileds = line.Split(',');

                var filedLength = fileds.Length;
                bool isNullRow = true;
                for (int j = 0; j < filedLength; j++)
                {
                    if (!string.IsNullOrEmpty(fileds[j]))
                    {
                        isNullRow = false;
                        break;
                    }
                }
                if (isNullRow)
                    continue;

                CSVFiledInfo lastinfo = null;
                CSVClassData csvinfo = new CSVClassData();
                csvinfo.fileds = new CSVFiledInfo[lsfiledInfo.Count];
                int index = 0;
                for (int col = 0; col < filedLength; col++)
                {
                    var filedInfo = FindFiled(lsfiledInfo, col);
                    if (null == filedInfo)
                        throw new Exception($"列数超出 字段信息最大索引");

                    if (null == lastinfo)
                    {
                        lastinfo = filedInfo.Clone();
                        csvinfo.fileds[index++] = lastinfo;
                    }
                    else if (lastinfo.Index != filedInfo.Index)
                    {
                        lastinfo = filedInfo.Clone();
                        csvinfo.fileds[index++] = lastinfo;
                    }

                    var filedValue = fileds[col];

                    if (string.IsNullOrEmpty(filedValue))
                    {
                        if (IsBackUp)
                        {
                            var row = i - 2;
                            if (row != 0)
                            {
                                for (int f = row - 1; f >= 0; f--)
                                {
                                    var lsinfo = lsCsvClass[f];
                                    var haveFiles = lsinfo.fileds[index - 1];
                                    string value;
                                    if (haveFiles.IsArray)
                                        value = haveFiles.Put(lastinfo.GetValueIndex);

                                    else
                                        value = haveFiles.value;

                                    if (!string.IsNullOrEmpty(value))
                                    {
                                        filedValue = value;
                                        break;
                                    }
                                }
                            }
                            lastinfo.Push(filedValue);
                        }
                    }
                    else
                    {
                        lastinfo.Push(filedValue);
                    }
                }
                lsCsvClass.Add(csvinfo);
            }

            return lsCsvClass;
        }


        private string GetFileType(string type, bool isArray)
        {
            var lower = type.ToLower();
            string _type = lower;
            if (lower.Equals("color"))
                _type = "Color";

            if (isArray)
                _type += "[]";
            return _type;
        }

        private string ToV2(string str)
        {
            if (string.IsNullOrEmpty(str)) return "Vector2.zero";

            var arge = str.Split(':');
            if (arge.Length != 2) return "Vector2.zero";
            return $"new Vector2({arge[0]}f,{arge[1]}f)";
        }
        private string ToV3(string str)
        {
            if (string.IsNullOrEmpty(str)) return "Vector3.zero";
            var arge = str.Split(':');

            if (arge.Length != 3) return "Vector3.zero";
            return $"new Vector3({arge[0]}f,{arge[1]}f,{arge[2]}f)";
        }
        protected string ToColor(string str)
        {
            if (string.IsNullOrEmpty(str))
                return "new Color(1,1,1)";

            //if (ColorUtility.TryParseHtmlString("#" + str, out Color color))
            //    return $"new Color32( {color.r.ToString("X2")},{color.g.ToString("X2")},{color.b.ToString("X2")},{color.a.ToString("X2")})";

            return "new Color(1,1,1)";
        }
        private string GetFileValue(string type, string value)
        {
            switch (type.ToLower())
            {
                case "color":
                    return ToColor(value);
                case "string":
                    if (string.IsNullOrEmpty(value))
                        return "\"\"";
                    if (value.Contains("\\"))
                        value = value.Replace("\\", "/");
                    return $"\"{value}\"";
                case "float":
                    if (string.IsNullOrEmpty(value))
                        return "0";
                    return $"{value}f";
                case "double":
                    if (string.IsNullOrEmpty(value))
                        return "0";
                    return $"{value}d";
                case "int":
                    if (string.IsNullOrEmpty(value))
                        return "0";
                    return value;
                case "v2":
                    return ToV2(value);
                case "v3":
                    return ToV3(value);
                case "bool":
                    if (string.IsNullOrEmpty(value))
                        return "false";
                    return value.ToLower();
                default:
                    if (string.IsNullOrEmpty(value))
                        return string.Empty;
                    return value;
            }
        }
        private string GetFileValue(CSVFiledInfo filed)
        {
            if (null == filed)
                return string.Empty;

            if (filed.IsArray)
            {
                StringBuilder str = new StringBuilder();

                int filedLength = filed.Length;

                // 检测有效长度
                for (int i = 0; i < filedLength; i++)
                {
                    if (filed.values == null || string.IsNullOrEmpty(filed.values[i]))
                    {
                        filedLength = i;
                        break;
                    }
                }

                str.Append($"new {GetFileType(filed.Type, false)}[{filedLength}]");
                str.AppendN("{");

                for (int i = 0; i < filedLength; i++)
                {
                    string ext = ",";
                    if (i == filedLength - 1 || string.IsNullOrEmpty(filed.values[i + 1]))
                        ext = "";

                    string value = GetFileValue(filed.Type, filed.values[i]);
                    if (!string.IsNullOrEmpty(value))
                        str.AppendTN(GetFileValue(filed.Type, filed.values[i]) + ext, 5);
                    else
                        break;
                }

                str.AppendT("}", 4);

                return str.ToString();

            }
            else
            {
                return GetFileValue(filed.Type, filed.value);
            }

        }
        /// <summary>
        /// 添加数据类字段
        /// </summary>
        private void AddDataClassFiled(StringBuilder str, List<CSVFiledInfo> lsinfo)
        {
            for (int i = 0; i < lsinfo.Count; i++)
            {
                var info = lsinfo[i];
                str.AppendTN($"public {GetFileType(info.Type, info.IsArray)} {info.Name};", 2);
            }

        }
        /// <summary>
        /// 添加数据类数据
        /// </summary>
        private void AddDataClassData(StringBuilder str, string className, List<CSVClassData> lsinfo)
        {

            int cdLength = lsinfo.Count;
            for (int i = 0; i < cdLength; i++)
            {
                var cla = lsinfo[i];
                int length = cla.fileds.Length;
                str.AppendTN($"Data.Add(new {className}Data()", 2);
                str.AppendTN("{", 2);
                for (int j = 0; j < length; j++)
                {
                    var filed = cla.fileds[j];
                    var value = GetFileValue(filed);
                    string ext = ",";
                    if (j == length - 1)
                        ext = "";
                    str.AppendTN($"{filed.Name} = {value}{ext}", 3);
                }

                str.AppendTN("});", 2);
            }
        }


        private string ToOthetString(string str)
        {
            return Regex.Replace(str, @"[^a-zA-Z0-9\u4e00-\u9fa5_]", "");
        }

        /// <summary>
        /// CSV 文件转脚本配置文件
        /// </summary>
        /// <param name="lines">csv 数据行</param>
        /// <param name="outFileName">输出文件名</param>
        /// <param name="outPath">输出文件路径</param>
        /// <param name="inheritClass">继承类</param>
        /// <param name="inheritClassData">继承数据类</param>
        /// <returns></returns>
        public string CreateCSVConfigFile(string[] lines, string outFileName, string outPath, string inheritClass = "CSVBaseConfig", string inheritClassData = "")
        {

            string[] newLines;
            if (IgnoreNum > 0)
            {
                newLines = new string[lines.Length - IgnoreNum];
                Array.Copy(lines, IgnoreNum, newLines, 0, newLines.Length);
            }
            else
            {
                newLines = lines;
            }



            if (string.IsNullOrEmpty(inheritClass))
                inheritClass = "CSVBaseConfig";


            outFileName = ToOthetString(outFileName);
            var filedInfo = ReadFiled(newLines);
            var classData = ParseLine(newLines, filedInfo);


            StringBuilder str = new StringBuilder();
            str.AppendN("/****************************************************");
            str.AppendN("*");
            str.AppendN("*         本文件由CSV 文件生成工具自动生成");
            str.AppendN($"*         {outFileName}.csv  {outFileName}.cs ");
            str.AppendN("*");
            str.AppendN("*****************************************************/");

            str.AppendN($"public class {outFileName} : {inheritClass}<{outFileName},{outFileName}.{outFileName}Data>");
            str.AppendN("{");

            if (string.IsNullOrEmpty(inheritClassData))
            {
                str.AppendTN($"public class {outFileName}Data");
            }
            else
            {
                // 父类必须具有Id字段
                var index = filedInfo.FindIndex(na => na.Name == "Id");
                if (index != -1)
                    filedInfo.RemoveAt(index);
                else
                {
                    throw new Exception($"{inheritClassData} 必须具有 Id 字段");
                }
                str.AppendTN($"public class {outFileName}Data : {inheritClassData}");
            }

            str.AppendTN("{");
            // 填充数据量结构
            AddDataClassFiled(str, filedInfo);

            str.AppendTN("}");

            str.AppendTN("protected override void Init()");
            str.AppendTN("{");
            // 填充类数据
            AddDataClassData(str, outFileName, classData);
            str.AppendTN("}");
            str.AppendN("}");


            string filepath = string.Empty;
            if (!outPath.EndsWith("\\") && !outPath.EndsWith("/"))
                filepath = $"{outPath}\\{outFileName}.cs";
            else
                filepath = $"{outPath}{outFileName}.cs";

            if (File.Exists(filepath))
                File.Delete(filepath);

            File.WriteAllText(filepath, str.ToString(), Encoding.UTF8);

            return filepath;
        }

    }
}
